use crate::{capturer::Capturer, cv::Cv};
use opencv::{
  core::{has_non_zero, Mat, MatTraitConst, Rect},
  imgcodecs,
  imgproc::{cvt_color_def, threshold, COLOR_BGR2GRAY, COLOR_BGRA2BGR, THRESH_BINARY},
};
use std::{error::Error, mem::size_of, thread::sleep, time::Duration};
use windows::Win32::{
  Foundation::*,
  Graphics::Gdi::{GetDC, GetPixel},
  UI::{
    Input::KeyboardAndMouse::{
      keybd_event, SendInput, INPUT, INPUT_MOUSE, KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP,
      MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MOVE,
      MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, VK_C, VK_D, VK_F, VK_SPACE, VK_X,
    },
    WindowsAndMessaging::*,
  },
};

#[derive(PartialEq, Debug)]
pub enum UIState {
  Default,
  Esc,
  Tab,
}

/** SCUM 窗口 */
pub struct Window {
  /** 句柄 */
  pub hwnd: HWND,
  /** 窗口左侧坐标 */
  pub x: u32,
  /** 窗口顶部坐标 */
  pub y: u32,
  /** 窗口宽度 */
  pub w: u32,
  /** 窗口高度 */
  pub h: u32,
  /** 窗口边框大小 */
  padding: RECT,
  esc_temp: Mat,
  capturer: Capturer,
}

#[allow(non_upper_case_globals)]
static mut WINHWD: HWND = HWND(0);

impl Window {
  pub fn new(imgs_path: String) -> Result<Window, Box<dyn Error>> {
    unsafe {
      let _ = EnumWindows(Some(enum_window), LPARAM(0))?;
      if WINHWD.eq(&HWND(0)) {
        return Err("未找到游戏窗口 请启动游戏".into());
      }
      let mut info = WINDOWINFO {
        cbSize: core::mem::size_of::<WINDOWINFO>() as u32,
        ..Default::default()
      };
      GetWindowInfo(WINHWD, &mut info)?;
      let is_caption = info.dwStyle.contains(WS_CAPTION);
      let _ = SetForegroundWindow(WINHWD);
      SetWindowPos(
        WINHWD,
        HWND_TOP,
        0,
        0,
        if is_caption { 1296 } else { 1280 },
        if is_caption { 759 } else { 720 },
        SWP_SHOWWINDOW | SWP_NOZORDER,
      )?;

      GetWindowInfo(WINHWD, &mut info)?;
      // debug!("{:?}", info);

      // 读取驾驶判断模板
      let mut tmp_str = String::from(imgs_path.clone());
      tmp_str += "/temp_esc.jpg";
      let esc_temp = imgcodecs::imread(&tmp_str, imgcodecs::IMREAD_GRAYSCALE)?;

      let capturer = Capturer::new()?;
      Ok(Window {
        hwnd: WINHWD,
        x: info.rcWindow.left as u32,
        y: info.rcWindow.top as u32,
        w: (info.rcWindow.right - info.rcWindow.left) as u32,
        h: (info.rcWindow.bottom - info.rcWindow.top) as u32,
        padding: RECT {
          left: info.rcClient.left - info.rcWindow.left,
          top: info.rcClient.top - info.rcWindow.top,
          right: info.rcWindow.right - info.rcClient.right,
          bottom: info.rcWindow.bottom - info.rcClient.bottom,
        },
        esc_temp,
        capturer,
      })
    }
  }

  pub fn sleep(&self, ms: u64) {
    sleep(Duration::from_millis(ms));
  }

  /** 截屏 */
  pub fn capture(&self) -> Result<Mat, Box<dyn Error>> {
    let img = self.capturer.capture_frame()?;
    let roi = img.roi(Rect::new(
      self.x as i32 + self.padding.left,
      self.y as i32 + self.padding.top,
      self.w as i32 - self.padding.right - self.padding.left,
      self.h as i32 - self.padding.bottom - self.padding.top,
    ))?;
    let mut gray = Mat::default();
    cvt_color_def(&roi, &mut gray, COLOR_BGR2GRAY)?;
    return Ok(gray);
  }

  pub fn capture_rgb(&self) -> Result<Mat, Box<dyn Error>> {
    let img = self.capturer.capture_frame()?;
    let roi = img.roi(Rect::new(
      self.x as i32 + self.padding.left,
      self.y as i32 + self.padding.top,
      self.w as i32 - self.padding.right - self.padding.left,
      self.h as i32 - self.padding.bottom - self.padding.top,
    ))?;
    let mut rgb = Mat::default();
    cvt_color_def(&roi, &mut rgb, COLOR_BGRA2BGR)?;
    return Ok(rgb);
  }

  /** 切换UI状态 */
  pub fn ensure_ui(&self, ui: UIState, args: i32) -> Result<(), Box<dyn Error>> {
    let state = self.ui_state()?;
    sleep(Duration::from_millis(250));
    // println!("{:?} {:?}", state, ui);
    if ui != state {
      match ui {
        UIState::Esc => {
          self.tap_key("esc")?;
        }
        UIState::Tab => {
          if state == UIState::Esc {
            self.tap_key("esc")?;
          }
          self.tap_key("tab")?;
        }
        UIState::Default => {
          if state == UIState::Esc {
            self.tap_key("esc")?;
          } else if state == UIState::Tab {
            self.tap_key("tab")?;
          }
        }
      };
    }
    if ui == UIState::Tab {
      self.tap_key(&format!("{:?}", args))?;
    }
    sleep(Duration::from_millis(250));
    Ok(())
  }

  /** 点击键盘键位 */
  pub fn tap_key(&self, key: &str) -> Result<(), Box<dyn Error>> {
    let mut id: u8 = 0;
    match key {
      "tab" => {
        id = 9;
      }
      "esc" => {
        id = 27;
      }
      "1" => {
        id = 48 + 1;
      }
      "4" => {
        id = 48 + 4;
      }
      "0" => {
        id = 48 + 0;
      }
      "9" => {
        id = 48 + 9;
      }
      "8" => {
        id = 48 + 8;
      }
      "f" => {
        id = 70;
      }
      "3" => {
        id = 48 + 3;
      }
      "w" => {
        id = 87;
      }
      "c" => {
        id = VK_C.0 as u8;
      }
      "x" => {
        id = VK_X.0 as u8;
      }
      "space" => {
        id = 32;
      }
      _ => {}
    };
    if id != 0 {
      // println!("tap: {}", key);
      unsafe {
        keybd_event(id, 0, KEYEVENTF_EXTENDEDKEY, 0);
        sleep(Duration::from_millis(25));
        keybd_event(id, 0, KEYEVENTF_KEYUP, 0);
        sleep(Duration::from_millis(50));
      };
    }

    Ok(())
  }

  /** 按下键盘键位 */
  pub fn press_key(&self, key: &str) -> Result<(), Box<dyn Error>> {
    let mut id: u8 = 0;
    match key {
      "a" => {
        id = 65;
      }
      "s" => {
        id = 83;
      }
      "w" => {
        id = 87;
      }
      "d" => {
        id = VK_D.0 as u8;
      }
      "tab" => {
        id = 9;
      }
      _ => {}
    }
    if id != 0 {
      // println!("press: {}", key);
      unsafe {
        keybd_event(id, 0, KEYEVENTF_EXTENDEDKEY, 0);
      };
    }
    Ok(())
  }

  /** 释放键盘键位 */
  pub fn release_key(&self, key: &str) -> Result<(), Box<dyn Error>> {
    let mut id: u8 = 0;
    match key {
      "a" => {
        id = 65;
      }
      "s" => {
        id = 83;
      }
      "w" => {
        id = 87;
      }
      "d" => {
        id = VK_D.0 as u8;
      }
      "tab" => {
        id = 9;
      }
      "ctrl" => {
        id = 17;
      }
      _ => {}
    }
    if id != 0 {
      // println!("release: {}", key);
      unsafe {
        keybd_event(id, 0, KEYEVENTF_KEYUP, 0);
      };
    }
    Ok(())
  }

  /// 鼠标绝对移动
  /// * `x` 相对于图像工作区域左上角的水平位置
  /// * `y` 相对于图像工作区域左上角的垂直位置
  pub fn mouse_move_to(&self, x: i32, y: i32) -> Result<(), Box<dyn Error>> {
    let _x = x + self.x as i32 + self.padding.left;
    let _y = y + self.y as i32 + self.padding.top;
    unsafe {
      let mut input = INPUT::default();
      input.r#type = INPUT_MOUSE;
      input.Anonymous.mi.dx = ((_x as f32 / self.capturer.width as f32) * 65535 as f32) as i32;
      input.Anonymous.mi.dy = ((_y as f32 / self.capturer.height as f32) * 65535 as f32) as i32;
      input.Anonymous.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
      SendInput(&[input], size_of::<INPUT>() as i32);
    };
    Ok(())
  }

  /// 鼠标相对移动
  pub fn mouse_move(&self, x: i32, y: i32) -> Result<(), Box<dyn Error>> {
    let mut input = INPUT::default();
    input.r#type = INPUT_MOUSE;
    input.Anonymous.mi.dx = x;
    input.Anonymous.mi.dy = y;
    input.Anonymous.mi.dwFlags = MOUSEEVENTF_MOVE;
    unsafe {
      SendInput(&[input], size_of::<INPUT>() as i32);
    }
    Ok(())
  }

  pub fn mouse_position(&self) -> Result<(i32, i32), Box<dyn Error>> {
    let mut pos = POINT::default();
    unsafe { GetCursorPos(&mut pos) }?;
    Ok((
      pos.x - (self.x as i32 + self.padding.left),
      pos.y - (self.y as i32 + self.padding.top),
    ))
  }

  /// 鼠标左键单击
  pub fn mouse_click_l(&self) -> Result<(), Box<dyn Error>> {
    let mut input = INPUT::default();
    input.r#type = INPUT_MOUSE;
    input.Anonymous.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    unsafe {
      SendInput(&[input], size_of::<INPUT>() as i32);
      sleep(Duration::from_millis(35));
      input.Anonymous.mi.dwFlags = MOUSEEVENTF_LEFTUP;
      SendInput(&[input], size_of::<INPUT>() as i32);
    }
    Ok(())
  }

  /// 鼠标由键单击
  pub fn mouse_click_r(&self) -> Result<(), Box<dyn Error>> {
    let mut input = INPUT::default();
    input.r#type = INPUT_MOUSE;
    input.Anonymous.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
    unsafe {
      SendInput(&[input], size_of::<INPUT>() as i32);
      sleep(Duration::from_millis(25));
      input.Anonymous.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
      SendInput(&[input], size_of::<INPUT>() as i32);
    }
    Ok(())
  }

  /// 鼠标左键按下
  pub fn mouse_press_l(&self) -> Result<(), Box<dyn Error>> {
    let mut input = INPUT::default();
    input.r#type = INPUT_MOUSE;
    input.Anonymous.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    unsafe {
      SendInput(&[input], size_of::<INPUT>() as i32);
      sleep(Duration::from_millis(15));
    }
    Ok(())
  }

  /// 鼠标左键按下
  pub fn mouse_release_l(&self) -> Result<(), Box<dyn Error>> {
    let mut input = INPUT::default();
    input.r#type = INPUT_MOUSE;
    input.Anonymous.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    unsafe {
      SendInput(&[input], size_of::<INPUT>() as i32);
      sleep(Duration::from_millis(15));
    }
    Ok(())
  }

  /// 判断某个坐标点是否是亮色
  pub fn is_active(&self, x: i32, y: i32) -> bool {
    unsafe {
      let color = GetPixel(
        GetDC(HWND(0)),
        x + self.x as i32 + self.padding.left,
        y + self.y as i32 + self.padding.top,
      )
      .0;
      let cc = ((color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF)) / 3;
      // println!("{:#X} {:#X}", cc, color);
      return cc > 0x50;
    }
  }

  pub fn reset(&self) -> Result<(), Box<dyn Error>> {
    let mut info = WINDOWINFO {
      cbSize: core::mem::size_of::<WINDOWINFO>() as u32,
      ..Default::default()
    };
    unsafe {
      GetWindowInfo(self.hwnd, &mut info)?;
      let is_caption = info.dwStyle.contains(WS_CAPTION);
      let _ = SetForegroundWindow(self.hwnd);
      SetWindowPos(
        self.hwnd,
        HWND_TOP,
        0,
        0,
        if is_caption { 1296 } else { 1280 },
        if is_caption { 759 } else { 720 },
        SWP_SHOWWINDOW | SWP_NOZORDER,
      )?;
    }
    Ok(())
  }

  /** 获取当前UI状态 */
  pub fn ui_state(&self) -> Result<UIState, Box<dyn Error>> {
    let gray = self.capture()?;
    let mut thres = Mat::default();
    threshold(&gray, &mut thres, 192.0, 255.0, THRESH_BINARY)?;
    let mut roi = thres.roi(Rect::new(480, 220, 320, 280))?;
    let (is_match, _) = Cv::is_match(&roi, &self.esc_temp)?;
    if is_match {
      return Ok(UIState::Esc);
    }
    roi = thres.roi(Rect::new(1200, 0, 50, 30))?;
    let res = has_non_zero(&roi)?;
    // tab
    if !res {
      return Ok(UIState::Tab);
    }
    Ok(UIState::Default)
  }

  /// 用于外部调用
  pub fn mouse_click(x: f32, y: f32) {
    unsafe {
      let mut input = INPUT::default();
      input.r#type = INPUT_MOUSE;
      input.Anonymous.mi.dx = (x * 65535 as f32) as i32;
      input.Anonymous.mi.dy = (y * 65535 as f32) as i32;
      input.Anonymous.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
      SendInput(&[input], size_of::<INPUT>() as i32);
      sleep(Duration::from_millis(125));
      input = INPUT::default();
      input.r#type = INPUT_MOUSE;
      input.Anonymous.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
      SendInput(&[input], size_of::<INPUT>() as i32);
      sleep(Duration::from_millis(35));
      input.Anonymous.mi.dwFlags = MOUSEEVENTF_LEFTUP;
      SendInput(&[input], size_of::<INPUT>() as i32);
    };
  }

  pub fn set_win_pos(args: [i32; 4]) -> Option<String> {
    unsafe {
      if EnumWindows(Some(enum_window), LPARAM(0)).is_err() {
        return Some(String::from("枚举窗口失败"));
      }
      if WINHWD.eq(&HWND(0)) {
        return Some(String::from("未找到游戏窗口 请启动游戏"));
      }
      let _ = SetForegroundWindow(WINHWD);
      if SetWindowPos(
        WINHWD,
        HWND_TOP,
        args[0],
        args[1],
        args[2],
        args[3],
        SWP_SHOWWINDOW | SWP_NOZORDER,
      )
      .is_err()
      {
        return Some(String::from("设置窗口失败"));
      }
      None
    }
  }

  pub fn get_win_pos() -> Option<[i32; 4]> {
    unsafe {
      if EnumWindows(Some(enum_window), LPARAM(0)).is_err() {
        return None;
      }
      if WINHWD.eq(&HWND(0)) {
        return None;
      }
      let mut info = WINDOWINFO {
        cbSize: core::mem::size_of::<WINDOWINFO>() as u32,
        ..Default::default()
      };
      if GetWindowInfo(WINHWD, &mut info).is_err() {
        return None;
      }
      Some([
        info.rcClient.left,
        info.rcClient.top,
        info.rcClient.right - info.rcClient.left,
        info.rcClient.bottom - info.rcClient.top,
      ])
    }
  }

  /// 用于外部调用
  pub fn press_key_ex(key: &str) {
    let mut id: u8 = 0;
    match key {
      "f" => {
        id = VK_F.0 as u8;
      }
      "space" => {
        id = VK_SPACE.0 as u8;
      }
      _ => {}
    }
    if id != 0 {
      unsafe {
        keybd_event(id, 0, KEYEVENTF_EXTENDEDKEY, 0);
      };
    }
  }

  /// 用于外部调用
  pub fn release_key_ex(key: &str) {
    let mut id: u8 = 0;
    match key {
      "f" => {
        id = VK_F.0 as u8;
      }
      "space" => {
        id = VK_SPACE.0 as u8;
      }
      _ => {}
    }
    if id != 0 {
      unsafe {
        keybd_event(id, 0, KEYEVENTF_KEYUP, 0);
      };
    }
  }
}

/// 窗口枚举回调函数
extern "system" fn enum_window(window: HWND, _: LPARAM) -> BOOL {
  unsafe {
    let mut text: [u16; 512] = [0; 512];
    let len = GetWindowTextW(window, &mut text);
    let text = String::from_utf16_lossy(&text[..len as usize]);
    if text.starts_with("SCUM") && text.len() == 6 {
      WINHWD = window;
      // let mut info = WINDOWINFO {
      //   cbSize: core::mem::size_of::<WINDOWINFO>() as u32,
      //   ..Default::default()
      // };
      // GetWindowInfo(window, &mut info).unwrap();
      // OUTTER_RECT = info.rcWindow.clone();
      // INNER_RECT.left = info.rcClient.left - info.rcWindow.left;
      // INNER_RECT.top = info.rcClient.top - info.rcWindow.top;
      // INNER_RECT.right = info.rcWindow.right - info.rcClient.right;
      // INNER_RECT.bottom = info.rcWindow.bottom - info.rcClient.bottom;
    }
    true.into()
  }
}
